﻿package de.gameduell.framework.facebook {
	import de.gameduell.framework.facebook.reporting.ReportingEvent;
	import de.gameduell.facebook.service.gen.CatalogItem;
	import de.gameduell.framework.asset.Asset;
	import de.gameduell.framework.asset.AssetEvent;
	import de.gameduell.framework.asset.BinaryAsset;
	import de.gameduell.framework.debug.Debug;
	import de.gameduell.framework.facebook.communication.FacebookData;
	import de.gameduell.framework.facebook.communication.ServiceLayerCommunication;
	import de.gameduell.framework.facebook.event.CommunicationErrorEvent;
	import de.gameduell.framework.facebook.event.DonationURLEvent;
	import de.gameduell.framework.facebook.event.InventoryEvent;
	import de.gameduell.framework.facebook.reporting.FBBaseTracker;
	import de.gameduell.framework.locale.Locale;
	import de.gameduell.framework.locale.LocaleData;
	import de.gameduell.framework.queue.AssetAction;
	import de.gameduell.framework.util.FlashVars;
	import de.gameduell.framework.util.Tools;

	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.StatusEvent;
	import flash.external.ExternalInterface;
	import flash.geom.Point;
	import flash.net.LocalConnection;
	import flash.net.URLRequest;
	import flash.net.navigateToURL;
	import flash.system.Security;
	import flash.utils.Timer;
	import flash.utils.getTimer;

	public class FBBaseController extends EventDispatcher implements IFBBaseController{

		public static const JACKPOT_GAME_START:String = "JACKPOT_GAME_START";
		public static const JACKPOT_GAME_END:String = "JACKPOT_GAME_END";
		
		public static const JACKPOT_GAME:String = "JACKPOT_GAME";

		public static const SERVER_ERROR:String = "server_error";
		public static const GAME_END:String     = "gameFinished";
		public static const GAME_START:String   = "gameStarted";
		public static const TUTORIAL_START:String = "tutorialStarted";
		public static const TUTORIAL_FINISHED:String = "tutorialFinished";
		public static const OUT_OF_LIVES:String = "out_of_lives";
		public static const OUT_OF_MONEY:String = "out_of_money";
		public static const NEW_HIGHSCORE:String = "new_highscore";
		
		public static const DIALOG_PAYMENT:String = "payment";
				
		protected var _currentGameType:String;
		protected var localeData:LocaleData;
		protected var applicationAssets:Array;
		protected var pageSettings:Array;
		protected var _baseURL:String;
		protected var localConnection:LocalConnection;
		protected var feedbackConnection:LocalConnection;
		protected var connectionKey:String;
		protected var _lastGameResult:GameResult;
		protected var locale:Locale;
		protected var _communication:ServiceLayerCommunication;
		protected var appData:FBBaseApplicationData;
		protected var tracker:FBBaseTracker;
		protected var _scoreCap:int;
		protected var _gameState:FBBaseGameState;
		protected var _catalog:FBBaseItemCatalog;
		protected var _inventory:FBBaseInventory;
		protected var _gameIsRunning:Boolean;
		protected var _initTimer:Timer;
		protected var startTime:int;
		protected var timeTracked:Boolean;
		protected var _feedbackConnectionKey:String;
		protected var _featureList:FeatureList;
		protected var _trackingName:String;
		protected var _tutorialType:String;
		protected var _ingameType:String;

		public function FBBaseController() {
			Security.allowDomain("*");
			Security.allowInsecureDomain("*");
			timeTracked = false;
			startTime = getTimer();
			_featureList = new FeatureList();
			_gameIsRunning = false;
			locale = Locale.getInstance();
			localeData = LocaleData.getInstance();
			localeData.setValue("appid", FlashVars.getKeyValue("appid")); 
			applicationAssets = new Array();
			pageSettings = new Array();
			appData = new FBBaseApplicationData();
			_gameState = new FBBaseGameState();
			_catalog   = new FBBaseItemCatalog();
			_inventory = new FBBaseInventory();
			
			_communication = new ServiceLayerCommunication(this);
			_communication.addEventListener(CommunicationErrorEvent.COMMUNICATION_ERROR, onCommunicationError);
			
			doLocaleMapping();
			setupJavaScriptCallbacks();
			
			tracker = FBBaseTracker.getInstance();
		}
		
		public function redirectTo(anURL:String):void {
			var url:String    = anURL; 
			var window:String = "_blank";
			
			var req:URLRequest = new URLRequest(url);
			if (!ExternalInterface.available) {
				navigateToURL(req, window);
			} else {
				var strUserAgent:String = String(ExternalInterface.call("function() {return navigator.userAgent;}")).toLowerCase();
				Debug.trace("FBBaseController::redirectToStore: " + strUserAgent, Debug.WARNING);
				if(strUserAgent.indexOf("firefox") != -1){
					navigateToURL(req, window);	
				}else if(strUserAgent.indexOf("msie") != -1 && uint(strUserAgent.substr(strUserAgent.indexOf("msie") + 5, 3)) >= 7){
					navigateToURL(req, "_top");
				} else {
					navigateToURL(req, window);
				}
			}
		}
		
		public function get gameIsRunning():Boolean {
			return _gameIsRunning;
		}
		
		public function collectUserData(event:Event = null):void {
			_communication.requestInitializeClient();
		}

		public function setPermissions(permissionType:String):void {
			Tools.callJavaScript("FB.Connect.showPermissionDialog", permissionType);
		}
				
		public function closeAllFBDialogs(event:Event = null):void {
			Tools.callJavaScript("closedlg");
		}
		
		public function becomeAFan(event:Event = null):void {
			tracker.track("become_a_fan");
			Tools.callJavaScript("becomeAFan");
		}

		public function bookmarkApp(event:Event = null):void {
			tracker.track("bookmark_app");
			Tools.callJavaScript("bookmark");
		}

		public function inviteFriends(event:Event = null):void {
			tracker.track("invite_friends");
			Tools.callJavaScript("inviteFriends", localeData.getValue("headline"), localeData.getValue("buttontext"), localeData.getValue("requesttext"), localeData.getValue("requestbuttontext"));
		}

		public function openPaymentPopup(event:Event = null):void {
			tracker.track("open_payment");
			Tools.callJavaScript("buyCoins", "0", "0");
		}
		
		public function buyItem(aItem:CatalogItem):void {
			Debug.trace("FBBaseController::buyItem: " + aItem.title + " price -> "  + int(applicationData.facebookData.balance.balance) + ":" + aItem.price);
			if(int(applicationData.facebookData.balance.balance) >= aItem.price){
				inventory.addEventListener(InventoryEvent.INVENTORY_ITEM_ADDED, onItemBought);
				_communication.buyItem(aItem);
			}else{
				Debug.trace("FBBaseController::buyItem: NO MONEY");
				dispatchEvent(new Event(OUT_OF_MONEY));
			}
		}
		
		protected function setupFeedbackConnection():void {
			if(feedbackConnection == null){
				feedbackConnection = new LocalConnection();
				feedbackConnection.client = this;
				feedbackConnection.allowDomain("*");
				feedbackConnection.addEventListener(StatusEvent.STATUS, onFeedbackChannelStatus);
			}
		}
		
		protected function onFeedbackChannelStatus(event:StatusEvent):void {
			Debug.trace("Controller::onFeedbackChannelStatus " + event, Debug.WARNING);
		}
		
		/**
		 * Sends data to the game.
		 * @param: func - Functionname as a String
		 * @param: param - a parameter of any type
		 */
		public function sendFeedbackData(func:String, param:Object = ""):void{
			try{
				feedbackConnection.send(_feedbackConnectionKey, func, param);
			}catch(e:Error){
				Debug.trace("FBBaseController::lcSendData function: " + func + "; param: " + param + "; e:" + e.message, Debug.ERROR);
			}
		}
		
		/**
		 * LocalConnection - call it from game
		 * Sends all needed gameData
		 */
		 public function lcRequestGameData(aConnectionKey:String):void{
		 	Debug.trace("FBBaseController::lcRequestGameData");
		 	var array:Array = new Array();
		 	array.push(catalog.toJSON(featureList));
		 	array.push(facebookData.balance.balance);
		 	_feedbackConnectionKey = aConnectionKey;
		 	sendFeedbackData("lcSetGameData", array);
		}
		
		/**
		 * LocalConnection - call it from game
		 * Sends a itemCatalog as a JSON-String to the game
		 */
		public function lcRequestCatalog(param:String):void {
			sendFeedbackData("lcSetCatalog", catalog.toJSON(featureList));
		}

		public function lcFlashTrack(param:String = "unknown"):void {
			Debug.trace("FBBaseController::lcFlashTrack " + param);
			tracker.track(param);
		}

		public function lcRequestPayment(param:String = "unknown"):void {
			Debug.trace("FBBaseController::lcRequestPayment " + param);
			openPaymentPopup();
		}

		/**
		 * LocalConnection - call it from game
		 * Buys the item if possible. Calls a method "itemBought" at the games Controller
		 * @param itemId
		 */
		public function lcBuyItemByID(itemId:String):void {
			Debug.trace("FBBaseController::buyItemByID: itemId -> " + itemId);
			buyItem(catalog.getItemById(itemId));
		}
		
		protected function onItemBought(event:InventoryEvent):void {
			tracker.track("item_bought." + event.itemId);
			Debug.trace("FBBaseController::onItemBought: item -> " + event.itemId + " title: "+  event.itemTitle);
			sendFeedbackData("onItemBought", event.itemId);
		}

		public function askForHelp(event:Event = null):void {
			tracker.track("ask_for_help");
			localeData.setValue("rlImageUrl", _baseURL + FlashVars.getKeyValue("imageUrl") + "life_request.jpg");
			_communication.requestGenerateDonationURL("lives_donation", "1");
			_communication.addEventListener(DonationURLEvent.DONATION_URL_RECEIVED, onDonationUrl);
		}
		
		public function onDonationUrl(event:DonationURLEvent):void {
			Debug.trace("FBBaseController::onDonationUrl: URL=" + event.donationURL, Debug.DEBUG);
			_communication.removeEventListener(DonationURLEvent.DONATION_URL_RECEIVED, onDonationUrl);
			
			localeData.setValue("donationURL", event.donationURL);
			var actionlink:String = locale.loadString("askForHelp.actionlink", true);
			var attachment:String = locale.loadString("askForHelp.attachment", true);
			Tools.callJavaScript("postToWall", attachment, actionlink);
		}
		
		public function postRankToWall(aRankString:String):void {
			tracker.track("post_rank_to_wall");
			var actionlink:String = locale.loadString("rankPublishDialog.actionlink", true);
			var attachment:String = locale.loadString("rankPublishDialog.attachment", true);
			Tools.callJavaScript("postToWall", attachment, actionlink);
		}

		public function postScoreToWall(event:Event = null):void {
			tracker.track("post_score_to_wall");
			var actionlink:String = locale.loadString("publishDialog.actionlink", true);
			var attachment:String = locale.loadString("publishDialog.attachment", true);
			Tools.callJavaScript("postToWall", attachment, actionlink);
		}

		public function get catalog():FBBaseItemCatalog {
			return _catalog;
		}
		
		public function get appAssetList():Array {
			return applicationAssets;
		}

		public function get lastGameResult():GameResult {
			return _lastGameResult;
		}
		
		public function getPageSettingsById(pageId:String):PageSettings {
			return PageSettings(pageSettings[pageId]);
		}

		public function loadAsset(aPageId:String):void {
			Debug.trace("FBBaseController::loadPage: " + (_baseURL + applicationAssets[aPageId]));
			var loader:Asset = new BinaryAsset(aPageId, _baseURL + applicationAssets[aPageId]);
			loader.addEventListener(AssetEvent.COMPLETE, onLoadingComplete);
			loader.addEventListener(AssetEvent.LOAD_ERROR, onLoadError);
			new AssetAction(loader, true, aPageId);
		}
		
		public function startJackpotGame(gameType:String, width:int, height:int, divXPos:int, divYPos:int):void {

		}
		
		public function getJackPotGame(event:Event = null):void {
			Debug.trace("FBBaseController::getJackPotGame: " + applicationData.jackpot.currentJackpotId);
			if(applicationData.jackpot.currentJackpotId == ""){
				_communication.requestGetCurrentJackPotGame();
			}else{
				_communication.requestFetchCurrentJackPotGame();
			}
		}

		protected function updateJackpotScore(aResultString:String, aTaskId:String = "none"):void {
			tracker.track("end_jackpot_game");
			_lastGameResult = new GameResult(aResultString, aTaskId);
			Debug.trace("FBBaseController::updateJackpotScore: jackpot score=" + _lastGameResult.score, Debug.DEBUG);
			communication.requestUpdateJackPotScore(_lastGameResult.score);
			dispatchEvent(new Event(JACKPOT_GAME_END));
		}

		public function registerScore(aResultString:String):void {
			Debug.trace("FBBaseController::registerScore: score=" + aResultString, Debug.DEBUG);
			_lastGameResult = new GameResult(aResultString, "aTaskId");
			localeData.setValue("score", String(_lastGameResult.score));
			_communication.requestUpdateScore(_lastGameResult.score);
			dispatchEvent(new Event(GAME_END));
			Tools.callJavaScript("closeGame");
		}

		public function startGame(gameType:String, width:int, height:int, divXPos:int, divYPos:int):void {
			_communication.requestStartGame();
			setupLocalConnection();
			Debug.trace("FBBaseController::startGame: " + gameType + ":" + width + ":" + height + ":" + "en" + ":" + connectionKey);
			dispatchEvent(new Event(GAME_START));
			Tools.callJavaScript("startGame", gameType, width, height, "en", connectionKey, divXPos, divYPos, "", "");
		}
		
		public function get trackingName():String {
			return _trackingName;
		}
		
		public function get ingameType():String {
			return _ingameType;
		}
		
		public function get tutorialType():String {
			return _tutorialType;
		}

		public function parseApplicationSettings(aXML:XML, aBaseURL:String):void {
			_baseURL = aBaseURL;
			
			_trackingName = String(aXML.child("trackingName").@name);
			_ingameType   = String(aXML.child("ingameType").@type);
			_tutorialType = String(aXML.child("tutorialType").@type);

			tracker.appType = _trackingName;
			tracker.track("done");
			
			var layoutSettings:XML = XML(aXML.child("layoutSettings"));
			var xPos:int = int(layoutSettings.@xPos);
			var yPos:int = int(layoutSettings.@yPos);
			FBBaseAppLayout.getInstance().contentPosition = new Point(xPos, yPos);
			
			var onDemand:XMLList = aXML.child("pages").children();
			var onDemandNode:XML;
			for(var j:* in onDemand){
				onDemandNode = XML(onDemand[j]);
				applicationAssets[onDemandNode.name()] = onDemandNode;
				pageSettings[onDemandNode.name()]  = new PageSettings(onDemandNode);
			}
			try {
				onDemand = aXML.child("assets").children();
				for(var k:* in onDemand) {
					onDemandNode = XML(onDemand[k]);
					applicationAssets[onDemandNode.name()] = onDemandNode;
				}
			}catch(e:Error) {
			}
		}

		protected function setupJavaScriptCallbacks():void {
			if (ExternalInterface.available && (Security.sandboxType != Security.LOCAL_WITH_FILE)){
				try {
					ExternalInterface.addCallback("fbDialogRemoved", fbDialogRemoved);
				} catch (error:SecurityError) {
					Debug.trace("FBBaseController::setupJavaScriptCallbacks: A SecurityError occurred: " + error.message, Debug.ERROR);
				} catch (error:Error) {
					Debug.trace("FBBaseController::setupJavaScriptCallbacks: An Error occurred: " + error.message, Debug.ERROR);
				}
			} else {
				Debug.trace("FBBaseController::setupJavaScriptCallbacks: External interface is not available for this container.", Debug.WARNING);
			}
		}
		
		public function fbDialogRemoved(dialogType:String = ""):void {
			Debug.trace("FBBaseController::fbDialogRemoved: dialogType -> " + dialogType, Debug.DEBUG);
			if(dialogType == DIALOG_PAYMENT){
				communication.requestUserBalance();
			}
			dispatchEvent(new FBDialogEvent(FBDialogEvent.CLOSED, dialogType));
		}
		
		protected function onCommunicationError(event:CommunicationErrorEvent):void {
			Debug.trace("FBBaseController::onCommunicationError: " + event.error, Debug.DEBUG);
			dispatchEvent(new CommunicationErrorEvent(event.error));
		}

		protected function doLocaleMapping():void {
			localeData.setValue("appURL", locale.loadString("appURL"));
			localeData.setValue("headline", locale.loadString("inviteDialog.headline"));
			localeData.setValue("buttontext", locale.loadString("inviteDialog.buttontext"));
			localeData.setValue("requesttext", locale.loadString("inviteDialog.requesttext"));
			localeData.setValue("requestbuttontext", locale.loadString("inviteDialog.requestbuttontext"));
			localeData.setValue("promotionMessage", "" + int(Math.random()*17)); // TODO - promo message count
		}

		protected function getRandomKey(amount:int):String {
			var keyset:String = "abcdefghijklmABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
			var randkey:String = "";
			for (var i:int = 0;i < amount; i++) {
				randkey += keyset.substr(Math.random() * (keyset.length - 1), 1);
			}
			return randkey;
		}
		
		public function stopInitTime():void {
			if(!timeTracked){
				timeTracked = true;
				var timeDiff:int = getTimer() - startTime;
				timeDiff = timeDiff < 0 ? 0 : timeDiff;
				timeDiff = int(timeDiff/1000);
				var mediaTime:int = int(int(FlashVars.getKeyValue(FlashVars.LOADING_TIME))/1000);
				
				mediaTime = mediaTime > 30 ? 30 : mediaTime;
				timeDiff = timeDiff > 30 ? 30 : timeDiff;
				
				tracker.track("time.init." + timeDiff);
				tracker.track("time.media." + mediaTime);

				Debug.trace("FBBaseController::stopInitTime   init: " + timeDiff + " media: " + mediaTime);
			}
		}

		protected function setupLocalConnection():void {
			if(localConnection != null) {
				localConnection.close();
				localConnection = null;
			}
			connectionKey = "_" + getRandomKey(5);
			localConnection = new LocalConnection();
			localConnection.addEventListener(StatusEvent.STATUS, onLocalConnectionStatusEvent);
			localConnection.client = this;
			localConnection.allowDomain("*");
			localConnection.allowInsecureDomain("*");
			try{
				Debug.trace("FBBaseController::setupLocalConnection:: connecting ... " + connectionKey, Debug.WARNING);
				localConnection.connect(connectionKey);
				localConnection.allowDomain("*");
			} catch(error:Error) {
				Debug.trace("FBBaseController::setupLocalConnection: Can't connect...the connection name is already being used by another SWF", Debug.ERROR);
			}
		}
		
		protected function onLocalConnectionStatusEvent(event:Event):void {
			Debug.trace("FBBaseController::onLocalConnectionStatusEvent", Debug.DEBUG);
		}

		protected function onLoadingComplete(event:AssetEvent):void {
			dispatchEvent(event.clone());
		}
		
		protected function onLoadError(event:AssetEvent):void {
			Debug.trace("FBBaseController::onLoadError: " + event.asset.id, Debug.ERROR);
		}
		
		public function get applicationData():FBBaseApplicationData {
			return appData;
		}
		
		public function get facebookData():FacebookData {
			return appData.facebookData;
		}
		
		public function get featureList():FeatureList{
			return _featureList;
		}
		
		public function get scoreCap():int {
			return _scoreCap;
		}
		
		public function set scoreCap(aScore:int):void {
			_scoreCap = aScore;
		}
		
		public function sendError():void {
		}
		
		public function get communication():ServiceLayerCommunication {
			return _communication;
		}
		
		public function get gameState():FBBaseGameState {
			return _gameState;
		}
		
		public function get inventory():FBBaseInventory {
			return _inventory;
		}
		
		public function set baseUrl(anURL:String):void {
			_baseURL = anURL;
			localeData.setValue("imageUrl", _baseURL + FlashVars.getKeyValue("imageUrl"));
			Debug.trace("FBBaseController::set baseUrl: imageUrl =  " + localeData.getValue("imageUrl"), Debug.DEBUG);
		}
		
		public function reportApplicationEvent(object:ReportingEvent):void {
			communication.requestFireEvent(object.eventType, object.eventDataAsJSON);
		}
	}
}